// NCR 53c8xx driver for Plan 9
// Nigel Roles (nigel@9fs.org)
//
// Microcode
//
// 27/5/02	Fixed problems with transfers >= 256 * 512
//
// 13/3/01	Fixed microcode to support targets > 7
//

extern	scsi_id_buf
extern	msg_out_buf
extern	cmd_buf
extern	data_buf
extern	status_buf
extern	msgin_buf
extern	dsa_0
extern  dsa_1
extern	dsa_head
extern	ssid_mask

SIR_MSG_IO_COMPLETE = 0
error_not_cmd_complete = 1
error_disconnected = 2
error_reselected = 3
error_unexpected_phase = 4
error_weird_message = 5
SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT = 6
error_not_identify_after_reselect = 7
error_too_much_data = 8
error_too_little_data = 9
SIR_MSG_REJECT = 10
SIR_MSG_SDTR = 11
SIR_EV_RESPONSE_OK = 12
error_sigp_set = 13
SIR_EV_PHASE_SWITCH_AFTER_ID = 14
SIR_MSG_WDTR = 15
SIR_MSG_IGNORE_WIDE_RESIDUE = 16
SIR_NOTIFY_DISC = 100
SIR_NOTIFY_RESELECT = 101
SIR_NOTIFY_MSG_IN = 102
SIR_NOTIFY_STATUS = 103
SIR_NOTIFY_DUMP = 104
SIR_NOTIFY_DUMP2 = 105
SIR_NOTIFY_SIGP = 106
SIR_NOTIFY_ISSUE = 107
SIR_NOTIFY_WAIT_RESELECT = 108
SIR_NOTIFY_ISSUE_CHECK = 109
SIR_NOTIFY_DUMP_NEXT_CODE = 110
SIR_NOTIFY_COMMAND = 111
SIR_NOTIFY_DATA_IN = 112
SIR_NOTIFY_DATA_OUT = 113
SIR_NOTIFY_BLOCK_DATA_IN = 114
SIR_NOTIFY_WSR = 115
SIR_NOTIFY_LOAD_SYNC = 116
SIR_NOTIFY_RESELECTED_ON_SELECT = 117
SIR_NOTIFY_LOAD_STATE = 118

STATE_FREE = 0
STATE_ALLOCATED = 1
STATE_ISSUE = 2
STATE_DISCONNECTED = 3
STATE_DONE = 4
STATE_END = 5

RESULT_OK = 0
	
MSG_IDENTIFY = 0x80
MSG_DISCONNECT = 0x04
MSG_SAVE_DATA_POINTER = 0x02
MSG_RESTORE_POINTERS = 0x03
MSG_IGNORE_WIDE_RESIDUE = 0x23
X_MSG = 0x01
X_MSG_SDTR = 0x01
X_MSG_WDTR = 0x03
MSG_REJECT = 0x07

BSIZE = 512
//BSIZE=4096

 // idle:
	jump	wait_for_reselection	
start:
	call	load_sync
//	move	13 to ctest0
//	int	SIR_NOTIFY_ISSUE
	clear	target
	select	atn from scsi_id_buf, reselected_on_select // do I need to clear ATN here?
	jump	start1, when msg_in	// why is this here?
start1:
//	move	14 to ctest0
	move	from msg_out_buf, when msg_out
id_out_mismatch:
	jump	start1, when msg_out		// repeat on parity grounds
	jump	to_decisions, when not cmd
cmd_phase:
//	int	SIR_NOTIFY_COMMAND
	clear	atn
	move	from cmd_buf, when cmd
cmd_out_mismatch:
	jump	to_decisions, when not data_in
data_in_phase:
	move	memory 4, state, scratcha
	move	memory 4, dmaaddr, scratchb
//	int	SIR_NOTIFY_DATA_IN
data_in_block_loop:
	move	scratcha2 to sfbr
	jump	data_in_normal, if 0
//	int	SIR_NOTIFY_BLOCK_DATA_IN
	move	BSIZE, ptr dmaaddr, when data_in		// transfer BSIZE bytes
data_in_block_mismatch:
	move	scratchb1 + BSIZE / 256 to scratchb1		// add BSIZE to scratchb
	move	scratchb2 + 0 to scratchb2 with carry
	move	scratchb3 + 0 to scratchb3 with carry
	move	scratcha2 + 255 to scratcha2			// sub one from block count
	move	memory 4, scratchb, dmaaddr			// save latest dmaddr
	jump	data_in_block_loop, when data_in
	move	memory 4, scratcha, state			// save latest state
	call	save_state
	jump	to_decisions
data_block_mismatch_recover:
	move	memory 4, scratchb, dmaaddr			// save latest dmaddr
data_mismatch_recover:
	move	memory 4, scratcha, state			// save latest state
	jump	to_decisions					// no need to save
								// as interrupt routine
								// did this
data_in_normal:
	move	scratcha3 to sfbr
	int	error_too_much_data, if not 0
	move	from data_buf, when data_in
data_in_mismatch:
	move	2 to scratcha3
	move	memory 4, scratcha, state
	call	save_state
	jump	post_data_to_decisions
data_out_phase:
//	int	SIR_NOTIFY_DATA_OUT
	move	memory 4, state, scratcha
	move	memory 4, dmaaddr, scratchb
data_out_block_loop:
	move	scratcha2 to sfbr
	jump	data_out_normal, if 0
	move	memory 4, dmaaddr, scratchb
	move	BSIZE, ptr dmaaddr, when data_out		// transfer BSIZE bytes
data_out_block_mismatch:
	move	scratchb1 + BSIZE / 256 to scratchb1		// add BSIZE to scratchb
	move	scratchb2 + 0 to scratchb2 with carry
	move	scratchb3 + 0 to scratchb3 with carry
	move	scratcha2 + 255 to scratcha2			// sub one from block count
	move	memory 4, scratchb, dmaaddr			// save latest dmaddr
	jump	data_out_block_loop, when data_out
	move	memory 4, scratcha, state			// save latest state
	jump	to_decisions
data_out_normal:
	move	scratcha3 to sfbr
	int	error_too_little_data, if not 0
	move	from data_buf, when data_out
data_out_mismatch:
	move	2 to scratcha3
	move	memory 4, scratcha, state
	call	save_state
	jump	post_data_to_decisions
status_phase:
	move	from status_buf, when status
//	int	SIR_NOTIFY_STATUS
	int	error_unexpected_phase, when not msg_in
msg_in_phase:
	move	1, scratcha, when msg_in
//	int	SIR_NOTIFY_MSG_IN
	jump	rejected, if MSG_REJECT
msg_in_not_reject:
	jump	disconnected, if MSG_DISCONNECT
	jump	msg_in_skip, if MSG_SAVE_DATA_POINTER
	jump	msg_in_skip, if MSG_RESTORE_POINTERS
	jump	ignore_wide, if MSG_IGNORE_WIDE_RESIDUE
	jump	extended, if X_MSG
	int	error_not_cmd_complete, if not 0
	move	scntl2&0x7e to scntl2			// take care not to clear WSR
	clear	ack
	wait	disconnect
	// update state
	move	memory 4, state, scratcha
	move	STATE_DONE to scratcha0
	move	RESULT_OK to scratcha1
	move	memory 4, scratcha, state
	call	save_state
//	int	SIR_MSG_IO_COMPLETE
	intfly	0
	jump	issue_check

rejected:
	int	SIR_MSG_REJECT
	clear	ack
	jump	to_decisions
msg_in_skip:
	clear	ack
	jump	to_decisions
	
extended:
	clear	ack
	int	error_unexpected_phase, when not msg_in
	move	1, scratcha1, when msg_in
	jump	ext_3, if 3
	jump	ext_2, if 2
	int	error_weird_message, if not 1
ext_1:
	clear	ack
	int	error_unexpected_phase, when not msg_in
	move	1, scratcha1, when msg_in
	jump	ext_done

ext_3:	clear	ack
	int	error_unexpected_phase, when not msg_in
	move	1, scratcha1, when msg_in
	clear	ack
	int	error_unexpected_phase, when not msg_in
	move	1, scratcha2, when msg_in
	clear	ack
	int	error_unexpected_phase, when not msg_in
	move	1, scratcha3, when msg_in
	move	scratcha1 to sfbr
	jump	ext_done, if not X_MSG_SDTR

// the target sent SDTR - leave ACK asserted and signal kernel
// kernel will either restart at reject, or continue
sdtr:	int	SIR_MSG_SDTR
	clear	ack
	jump	to_decisions

ext_2:	clear	ack
	int	error_unexpected_phase, when not msg_in
	move	1, scratcha1, when msg_in
	clear	ack
	int	error_unexpected_phase, when not msg_in
	move	1, scratcha2, when msg_in
	move	scratcha1 to sfbr
	jump	ext_done, if not X_MSG_WDTR

wdtr:	int	SIR_MSG_WDTR
	clear	ack
	jump	to_decisions

ext_done:
//	ought to check message here, but instead reject all
//	NB ATN set
reject:
	set	atn					// get target's ATN
	clear	ack					// finish ACK
	move	MSG_REJECT to scratcha			// prepare message
	int	error_unexpected_phase, when not msg_out// didn't get ATN
	clear	atn					// last byte coming
	move	1, scratcha, when msg_out		// send byte
	clear	ack					// finish ACK
	jump	reject, when msg_out			// parity error
	jump	to_decisions
	
ignore_wide:
	clear	ack
	int	error_unexpected_phase, when not msg_in
	move	1, scratcha1, when msg_in
	int	SIR_MSG_IGNORE_WIDE_RESIDUE
	clear	ack
	jump	to_decisions

//	sends a response to a message
response:
	set	atn
	clear	ack
	int	error_unexpected_phase, when not msg_out
response_repeat:
	move	from msg_out_buf, when msg_out
	jump	response_repeat, when msg_out		// repeat on parity grounds
// now look for response
// msg_in could be a REJECT
// anything other message is something else so signal kernel first
	jump	response_msg_in, when msg_in
	int	SIR_EV_RESPONSE_OK			// not a MSG_IN so OK
	jump	to_decisions

response_msg_in:
	move	1, scratcha, when msg_in
	jump	rejected, if MSG_REJECT		// go and generate rej interrupt
	int	SIR_EV_RESPONSE_OK		// not a REJECT so OK
	jump	msg_in_not_reject		// try others

disconnected:
//	move	5 to ctest0
	move	scntl2&0x7e to scntl2			// don't clear WSR
	clear 	ack
	wait	disconnect
	// UPDATE state to disconnected
	move	memory 4, state, scratcha
	move	STATE_DISCONNECTED to scratcha0
	move	memory 4, scratcha, state
	call	save_state
wsr_check:
	move	scntl2&0x01 to sfbr
	int	SIR_NOTIFY_WSR, if not 0
//	int	SIR_NOTIFY_DISC
	jump	issue_check

reselected_on_select:
	int	SIR_NOTIFY_RESELECTED_ON_SELECT
	jump	reselected

wait_for_reselection:
//	move	11 to ctest0
//	int	SIR_NOTIFY_WAIT_RESELECT
	wait reselect sigp_set
reselected:
//	move	12 to ctest0
	clear	target
	int	SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT, when not msg_in
	move	1, scratchb, when msg_in
	int	error_not_identify_after_reselect, if not MSG_IDENTIFY and mask 0x1f
//	int	SIR_NOTIFY_RESELECT
	// now locate the right DSA - note do not clear ACK, so target doesn't start
	// synchronous transfer until we are ready
find_dsa:
//	move	6 to ctest0
 	move	memory 4, dsa_head, dsa
find_dsa_loop:
//	move	7 to ctest0
//	move	8 to ctest0
	// load state from DSA into dsa_copy
	call	load_state
	move	memory 4, state, scratcha		// get dsastate in scratcha
	move	scratcha0 to sfbr			// and state variable in sfbr
	jump	find_dsa_next, if not STATE_DISCONNECTED // wrong state
	int	error_reselected, if STATE_END
	move	ssid & ssid_mask to sfbr			// get target ID
	move	memory 1, targ, find_dsa_smc1		// forge target comparison instruction
find_dsa_smc1:
	jump	find_dsa_next, if not 255		// jump if not matched
	move	memory 1, lun, find_dsa_smc2		// forge lun comparison instruction
	move	scratchb0 to sfbr			// recover IDENTIFY message
find_dsa_smc2:
	jump	reload_sync, if 255 and mask ~7		// off we jolly well go
find_dsa_next:
	move	memory 4, next, dsa			// find next
	jump	find_dsa_loop

// id_out terminated early
// most likely the message wasn't recognised
// clear ATN and accept the message in
// called from sd53c8xx.c directly
id_out_mismatch_recover:
	clear	atn
        jump    msg_in_phase, when msg_in
        int     SIR_MSG_REJECT
        jump    to_decisions

// Reload synchronous registers after a reconnect. If the transfer is a synchronous read, then
// as soon as we clear ACK, the target will switch to data_in and start blasting data into the
// fifo. We need to be executing the 'jump when data_in' instruction before the target stops REQing
// since it is the REQ which latches the 'when'. The target will do 16 REQs before stopping, so
// we have 16 bytes (160uS) plus delays to do this after clearing ACK. Once the jump is executing,
// the target will wait, so as much debugging as you like can happen in data_in_phase, just don't
// stick any delays between 'clear ack' and 'jump data_in_phase, when data_in'.

reload_sync:
	call	load_sync
	clear	ack
to_decisions:
	jump	data_in_phase, when data_in
	jump	cmd_phase, if cmd
	jump	data_out_phase, if data_out
	jump	status_phase, if status
	jump	msg_in_phase, if msg_in
	int	error_unexpected_phase

post_data_to_decisions:
	jump	status_phase, when status
	jump	msg_in_phase, if msg_in
	int	error_unexpected_phase
	
//
// MULTI_TARGET
//
// following must mirror top of dsa structure
// the first section is loaded and saved, the
// second section loaded only
dsa_copy:
state:	defw	0			// a0 is state, a1 result, a2 dma block count
dmaaddr: defw	0			// dma address for block moves
dsa_save_end:
targ:	defw	0			// lsb is target
lun:	defw	0			// lsb is lun
sync:	defw	0			// lsb is scntl3, sxfer
next:	defw	0
dsa_load_end:
dsa_load_len = dsa_load_end - dsa_copy
dsa_save_len = dsa_save_end - dsa_copy

load_state:
//	int	SIR_NOTIFY_LOAD_STATE
	jump load_state_okay

	move	dsa0 to sfbr
	jump load_state_okay, if not 0
	move	dsa1 to sfbr
	jump load_state_okay, if not 0
	move	dsa2 to sfbr
	jump load_state_okay, if not 0
	move	dsa3 to sfbr
	jump load_state_okay, if not 0
	// dsa is 0
	move	memory 4, dsa, dmaaddr
	move	memory 4, dsa, targ
	move	memory 4, dsa, lun
	move	memory 4, dsa, sync
	move	memory 4, dsa, next
	move	memory 4, dsa, scratcha
	move	STATE_END to sfbr
	move	sfbr to scratcha0
	move	memory 4, scratcha, state
	return

load_state_okay:
	// load state from DSA into dsa_copy
//	move	9 to ctest0
	move	memory 4, dsa, load_state_smc0 + 4
load_state_smc0:
	move	memory dsa_load_len, 0, dsa_copy
//	move	20 to ctest0
	return
save_state:
	move	memory 4, dsa, save_state_smc0 + 8
save_state_smc0:
	move	memory dsa_save_len, dsa_copy, 0
	return

sigp_set:
//	int	SIR_NOTIFY_SIGP
	move	ctest2 to sfbr				// clear SIGP
issue_check:
//	int	SIR_NOTIFY_ISSUE_CHECK
//	move	1 to ctest0
	move	memory 4, dsa_head, dsa
issue_check_loop:
 	call	load_state
	move	memory 4, state, scratcha		// get dsastate in scratcha
	move	scratcha0 to sfbr			// and state variable in sfbr
	jump	start, if STATE_ISSUE			// right state
	jump	wait_for_reselection, if STATE_END
 //	move	4 to ctest0
	move	memory 4, next, dsa			// find next
	jump	issue_check_loop


load_sync:
	move	memory 4, sync, scratcha		// load the sync stuff
	move	scratcha0 to sfbr			// assuming load_state has been called
	move	sfbr to scntl3
	move	scratcha1 to sfbr
	move	sfbr to sxfer
 //	int	SIR_NOTIFY_LOAD_SYNC
	return
